# Simple AJAX chess website - Game related handlers
#
# Copyright (C) 2008  Hong Anh Khoa
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from google.appengine.api import users

from basehandler import BaseRequestHandler
from datamodel import *
# from modules import chess
from modules.ChessBoard import ChessBoard

import pickle

class GameHandler(BaseRequestHandler):
  
  def usercolor(self, room):
    # Return piece color of the current user
    user = users.get_current_user()
    if user is not None:
      player = RoomPlayer.gql("WHERE room=:1 AND user=:2", room, user).get()
      if player is not None and player.color is not None:
        return player.color[0]
        
    return ""
    
  
  def get(self, roomid):
    # Get the changes
    room = Room.get(roomid)
    
    if RoomPlayer.gql("WHERE room=:1 AND status=:2", room, 'playing').count() == 2:
      # Playing. Returns: playing|white|black|FEN|usercolor|lastmove
      
      res = "playing|"
      
      game = Game.gql("WHERE room=:1 AND playing=:2", room, True).get()
      gameobj = pickle.loads(game.gameobject)
      
      # Put a class "playing" before current turn's player
      players = '<b class="playing">%s</b>|<b>%s</b>' if gameobj.getTurn() == ChessBoard.WHITE else '<b>%s</b>|<b class="playing">%s</b>'
      
      res += players % (game.white.nickname(), game.black.nickname())
      
      res += "|%s" % gameobj.getFEN()
      
      res += "|%s|" % self.usercolor(room)
      
      # Add last move
      cols = "abcdefgh"
      lmove = gameobj.getLastMove()
      
      if lmove is not None:
        lastmove = cols[lmove[0][0]].__str__() + (8 - lmove[0][1]).__str__() + "-" + cols[lmove[1][0]].__str__() + (8 - lmove[1][1]).__str__()
        res += "%s" % lastmove
      else:
        res += "-"
            
      self.response.out.write(res)


    elif RoomPlayer.gql("WHERE room=:1 AND status=:2", room, 'finished').count() == 2:
      # Game finished. Returns: finished|whitename|blackname|FEN|result
      game = Game.gql("WHERE room=:1 AND playing=:2", room, True).get()
      gameobj = pickle.loads(game.gameobject)
      
      fen = gameobj.getFEN()
      
      res = "finished|%s|%s|%s|%s" % (game.white.nickname(), game.black.nickname(), fen, game.result)
      
      self.response.out.write(res)
    
    else:
      # Not currently playing. Returns: waiting|whitename|blackname|usercolor
    
      res = "waiting|"
      wplayer = RoomPlayer.gql("WHERE room=:1 AND color=:2", room, "white").get()
      if wplayer is not None: res += ('%s' if wplayer.status != 'playing' else '<b>%s</b>') % wplayer.user.nickname() 

      res += "|"
      
      bplayer = RoomPlayer.gql("WHERE room=:1 AND color=:2", room, "black").get()
      if bplayer is not None: res += ('%s' if bplayer.status != 'playing' else '<b>%s</b>') % bplayer.user.nickname() 
      
      res += "|%s" % self.usercolor(room)
      
      self.response.out.write(res)
      
  
  def post(self, roomid):
    # Submit move

    move = self.request.get("move")
    room = Room.get(roomid)
    game = Game.gql("WHERE room=:1 AND playing=:2", room, True).get()
    gameobj = pickle.loads(game.gameobject)
    
    if gameobj.addTextMove(move) == False:
      # Can not parse move
      errstr = ["", "Invalid move", "Invalid color", "", "", "", "Game is over"][gameobj.getReason()]
      self.response.out.write("Err: %s" % errstr)
      return
      
    # No error
    
    # Update gameobject
    game.gameobject = db.Blob(pickle.dumps(gameobj))
    game.put()
    
    res = gameobj.getGameResult()
    
    if res != ChessBoard.NO_RESULT:
      # Game finished
      # Update status of the game
      if res == ChessBoard.WHITE_WIN:
        resstr = "wwin/White wins"
      elif res == ChessBoard.BLACK_WIN:
        resstr = "bwin/Black wins"  
      elif res == ChessBoard.STALEMATE:
        resstr = "draw/Draw by stalemate"
      elif res == ChessBoard.FIFTY_MOVES_RULE:
        resstr = "draw/Draw by fifty moves rule"
      elif res == ChessBoard.THREE_REPETITION_RULE:
        resstr = "draw/Draw by three repetition rule"
        
      game.result = resstr
      game.put()
      
      # Update status of players
      players = RoomPlayer.gql("WHERE room=:1 AND status=:2", room, "playing")
      for player in players:
        player.status = "finished"
        player.put()
        
    
    # Response: OK|FEN|Result
    self.response.out.write("OK|%s" % gameobj.getFEN())
    

class GameCommandHandler(BaseRequestHandler):

  def post(self, roomid):
    user = users.get_current_user()
    
    if user is None:
      self.response.out.write("Err: Only logged in user can play")
      return
  
    room = Room.get(roomid)
    
    # Process command
    comm = self.request.get("command")
    
    if comm == "black" or comm == "white":
      # Choose color command
      stat = RoomPlayer.gql("WHERE room=:1 AND user=:2", room, user).get()
      if stat.color is not None:
        self.response.out.write("Err: Already chosen color")
        return
      
      if RoomPlayer.gql("WHERE room=:1 AND color=:2", room, comm).get() is None:
        # Accept player choosing color
        pres = RoomPlayer.gql("WHERE room=:1 AND user=:2", room, user).get()
        pres.color = comm;
        pres.put()
        self.response.out.write("OK: %s" % user.nickname())
      else:
        self.response.out.write("Err: Opponent has already chosen %s." % comm)
  
    
    elif comm == "ncolor":
      # Cancel selecting color
      stat = RoomPlayer.gql("WHERE room=:1 AND user=:2", room, user).get()
      if stat.status == "playing":
        self.response.out.write("Err: Can not stand up while playing")
        return
      
      stat.color = None
      stat.put()
      
      self.response.out.write("OK")
      
        
    elif comm == "ready":
      # Ready command, switch status to "playing"
      stat = RoomPlayer.gql("WHERE room=:1 AND user=:2", room, user).get()
      if stat.color is None:
        self.response.out.write("Err: Must choose a color first")
        return
        
      stat.status = "playing"
      stat.put()
      
      self.response.out.write("OK")
      players = RoomPlayer.gql("WHERE room=:1 AND status=:2 ORDER BY color", room, "playing")
      if players.count() == 2:
        # Two player ready, start a new game
        
        gameObj = ChessBoard()
        
        game = Game(room = room,
                    white = players[1].user,
                    black = players[0].user,
                    gameobject = db.Blob(pickle.dumps(gameObj)),
                    playing = True)
        game.put()


    elif comm == "new":
      # New game command. Create a new game if current status is finished
      stat = RoomPlayer.gql("WHERE room=:1 AND user=:2", room, user).get()
      if stat.status == "playing":
        self.response.out.write("Err: Must finish current game first")
        return
        
      elif stat.status == "finished":
        # Mark current game as finished
        game = Game.gql("WHERE room=:1 AND playing=:2", room, True).get()
        game.playing = False
        game.put()
      
        # Create new game
        players = RoomPlayer.gql("WHERE room=:1", room)
        for player in players:
          player.status = None
          player.color = None
          player.put()
          
        self.response.out.write("OK")
        
    elif comm == "resign":
      # Resign command. End current game.
      
      game = Game.gql("WHERE room=:1 AND playing=:2", room, True).get()
      
      # Update user status
      players = RoomPlayer.gql("WHERE room=:1 AND status=:2", room, "playing")
      for player in players:
        player.status = "finished"
        player.put()
        
      # Update game status
      if user == game.white:
        resstr = "bwin/White resigned"
      else:
        resstr = "wwin/Black resigned"
        
      game.result = resstr
      game.put()
      
      self.response.out.write("OK")
      

class GameReplayHandler(BaseRequestHandler):

  def get(self, gameid):
    # Build replay page HTML
    import modules.utilities as utilities
        
    self.css = ['gamereplay.css']
    self.js = ['replay.js']
    
    # Get the game object
    game = Game.get(gameid)
    gameobj = pickle.loads(game.gameobject)
    
    pgn, movelength = self.getpgn(gameobj)
    self.inline_js = ['var movelength=%s;' % movelength]
    
    # Generate HTML content
    res = self.generate("gamereplay.tpl.html", {"chessboard": utilities.get_chessboard_html(), 
                                                "pgn": pgn,
                                                "black": game.black,
                                                "white": game.white})
    self.response.out.write(self.generate_page({"content": res}))
    
  
  def getpgn(self, gameobj):
    # Return the HTML of the PGN
    moves = gameobj.getAllTextMoves(ChessBoard.SAN)
    
    # Fullmoves = [(whitemove, blackwhite)]
    fullmoves = []
    id = 0;
    
    for move in moves:
      id = id + 1
      if len(fullmoves) == 0:
        fullmoves = [[id, move]]
      elif len(fullmoves[-1]) == 2:
        fullmoves[-1] += [id, move]
      else:
        fullmoves += [[id, move]]
          
    # Generate the HTML
    res = self.generate("movelist.tpl.html", {"moves": fullmoves})
    
    return res, len(moves)
    
  
  def post(self, gameid):
    # Return board position at a specified move
    move = int(self.request.get("move"))
    
    # Get the game object
    game = Game.get(gameid)
    gameobj = pickle.loads(game.gameobject)

    gameobj.gotoMove(move)
    
    lmove = gameobj.getLastMove()
    cols = "abcdefgh"
    
    if lmove is not None:
      lastmove = cols[lmove[0][0]].__str__() + (8 - lmove[0][1]).__str__() + "-" + cols[lmove[1][0]].__str__() + (8 - lmove[1][1]).__str__()
    else:
      lastmove = ""

    self.response.out.write("%s|%s" % (gameobj.getFEN(), lastmove))